import { useState, useRef } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useHistory, useLocation } from 'react-router-dom'
import { Toast } from 'antd-mobile'

import { useDispatch } from 'react-redux'
import { sendCode, login } from '@/store/actions'

import styles from './index.module.scss'

import Input from '@/components/Input'
import NavBar from '@/components/NavBar'
import { AxiosError } from 'axios'

const Login = () => {
  // 获取location
  // 注意：此处的泛型用来为 location.state 指定类型
  //      但是一定要注意：state 可能有值，也可以没有值，所以，它的类型中带有 undefined
  const location = useLocation<{ from: string } | undefined>()

  // 拿到 dispatch 分发动作的函数
  const dispatch = useDispatch()

  // 主动调用 useHistory() 来获取路由的 history 对象
  const history = useHistory()
  // 倒计时的时间，单位是 秒
  const [time, setTime] = useState(0)
  // 定时器 id
  const timerIdRef = useRef(-1)
  // 用来存储手机号码对应的文本框 DOM 元素
  const mobileRef = useRef<HTMLInputElement>(null)

  // useForm Hook 的参数是一个对象，也就是配置对象
  const formik = useFormik({
    // 表单项的默认值
    initialValues: {
      mobile: '',
      code: ''
    },

    // 表单验证，此处，我们使用 Yup 包来进行验证
    validationSchema: Yup.object({
      // required() 的参数就是：验证失败时的错误信息
      mobile: Yup.string()
        .required('手机号为必填项')
        .matches(/^1[3-9]\d{9}$/, '手机号码格式不正确'),
      code: Yup.string()
        .required('验证码为必填项')
        .matches(/^\d{6}$/, '验证码不正确')
    }),

    // 表单提交事件
    // 登录的逻辑代码写在此处
    onSubmit: async values => {
      try {
        // 此处，使用 await 可以做到：等到 dispatch 分发的异步action执行完成后，
        // 再执行后面的代码
        // dispatch 后会执行一个异步操作
        await dispatch(login(values))

        // 如果异步操作成功了，会执行改代码
        Toast.info('登录成功', 1, () => {
          // 清理定时器
          clearInterval(timerIdRef.current)

          // 判断 state 是否有值
          if (location.state?.from) {
            // 重定向过来的，就返回原来的页面
            history.replace(location.state.from)
          } else {
            // 默认，返回首页
            history.replace('/home')
          }
        })
      } catch (e) {
        // 处理 axios 请求的错误
        const error = e as AxiosError<{ message: string }>
        // 如果异步操作失败了，会执行此处的错误处理
        Toast.info(error.response?.data?.message, 2)
      }
    }
  })

  // 发送请求获取验证码
  const getCode = async () => {
    // 判断剩余秒数，如果剩余秒数小于 0 就允许再次发送
    // 如果剩余秒数大于等于 0 就不允许再次发送
    if (time > 0) return

    const { mobile } = formik.values
    // 使用正则来校验手机号码
    // 如果手机号码验证不正确，此处，就提示用户： 请输入正确的手机号
    if (!/^1[3-9]\d{9}$/.test(mobile)) {
      // 手机号码对应文本框 DOM 元素
      const mobileInputDOM = mobileRef.current
      // focus() 就是用来获得焦点的一个方法，是 DOM 元素自己提供的
      mobileInputDOM?.focus()
      return Toast.info('请输入正确的手机号', 1)
    }

    // 将倒计时时间，更新为：60
    setTime(59)
    clearInterval(timerIdRef.current)
    timerIdRef.current = window.setInterval(() => {
      setTime(time => {
        if (time === 1) {
          clearInterval(timerIdRef.current)
        }
        return time - 1
      })
    }, 1000)

    // 手机号码验证成功：
    dispatch(sendCode(mobile))
  }

  // handleSubmit 处理表单的提交事件
  const {
    handleSubmit,
    handleChange,
    handleBlur,
    values: { mobile, code },
    // 表示当前表单项是否被”摸过“（ 可以理解是否访问过 ）
    touched,
    // 表单验证失败时的错误信息
    errors,
    // 表示：表单验证是否成功
    isValid
  } = formik

  const isFirst = Object.keys(touched).length === 0

  return (
    <div className={styles.root}>
      {/* 导航栏组件 */}
      <NavBar onClick={() => history.go(-1)} />
      <h1 className="title">账号登录</h1>

      <div className="login-wrap">
        <form onSubmit={handleSubmit}>
          <div className="form-item">
            <Input
              name="mobile"
              value={mobile}
              onChange={handleChange}
              onBlur={handleBlur}
              type="text"
              placeholder="请输入手机号"
              dom={mobileRef}
            />
            <div className="error">
              {touched.mobile && errors.mobile ? (
                <div>{errors.mobile}</div>
              ) : null}
            </div>
          </div>
          <div className="form-item">
            <Input
              extra={time <= 0 ? '发送验证码' : `${time}s`}
              onExtraClick={getCode}
              name="code"
              value={code}
              onChange={handleChange}
              onBlur={handleBlur}
              type="text"
              placeholder="请输入验证码"
            />
            <div className="error">
              {touched.code && errors.code ? <div>{errors.code}</div> : null}
            </div>
          </div>
          <div className="form-item">
            <button
              type="submit"
              disabled={isFirst || !isValid}
              className={isFirst ? '' : isValid ? 'active' : ''}
            >
              登录
            </button>
          </div>
        </form>
      </div>
    </div>
  )
}

export default Login
